/* 
 * File:   IPTManager.cpp
 * Author: bharat
 * 
 * Created on October 21, 2009, 10:11 PM
 */

#include "P2Defs.h"

#include "IPTManager.h"
#include "../machine/machine.h"
#include "Process.h"
#include "../threads/system.h"
#include "../threads/synch.h"
#include "addrspace.h"
#include <time.h>

IPTManager::IPTManager() {
    DEBUG('I', "IPTManager: In IPTManager Constructor\n");
    nextEntryAge = 0;
    physPages = new BitMap(NumPhysPages);
    ipt = new IPTEntry[NumPhysPages];
    iptLock = new Lock("IPTManagerLock");
    for (int i = 0; i < NumPhysPages; i++) {
        ipt[i].valid = FALSE;
    }

    previousPID = -1;

    DEBUG('I', "IPTManager: IPTManager init complete. Max Processes: %d and Physical pages: %d\n", MAX_PROCESSES, NumPhysPages);
    printf("IPTManager: IPTManager init complete. Max Processes: %d and Physical pages: %d\n", MAX_PROCESSES, NumPhysPages);
}

void IPTManager::printIPT() {
    int i;
    if (DebugIsEnabled('W')) {
        printf("\n\nIPT:\n");

        for (i = 0; i < NumPhysPages; i++) {
            ipt[i].printIPTEntry();
        }
    }
}

IPTEntry* IPTManager::SearchIPT(int pid, int vpn) {
    int i = 0;
    IPTEntry* entry = NULL;


    iptLock -> Acquire();

    // linear-serach in IPT, Inefficient, but works for us.
    for (i = 0; i < NumPhysPages; i++) {
        if (ipt[i].pid == pid && ipt[i].virtualPage == vpn && ipt[i].valid == TRUE)
            break;
    }

    if (i < NumPhysPages) {
        //found an entry in IPT.
        entry = &ipt[i];
    }
    // releae the lock
    iptLock -> Release();

    return entry;
}

unsigned int IPTManager::getNextAge() {
    return nextEntryAge++;
}

int IPTManager::addIPTEntry(IPTEntry* nEntry) {


    iptLock -> Acquire();

    // DEBUG('D', "IPTManager: addIPTEntry start. PID=%d, VPN=%d\n", nEntry -> pid, nEntry->virtualPage);
    if (nEntry -> physicalPage >= NumPhysPages || nEntry -> physicalPage < 0) {
        // DEBUG('W', "IPTManager: Error in adding IPT entry. %d is invalid entry.\n", nEntry -> physicalPage);

        iptLock -> Release();
        return -1;
    }


    int ppn_ = nEntry -> physicalPage;

    /*
    if (nEntry -> valid == FALSE) {
        DEBUG('W', "IPTManager: ??? Addign an Entry that is invalid...\n");
    }

    if (ipt[ppn_].valid == TRUE) {
        DEBUG('W', "IPTManager: ??? IPTEntry %d is VALID entry.but you are trying to overwrite it...\n", nEntry -> physicalPage);
    }
     */

    ipt[ppn_].physicalPage = ppn_;
    ipt[ppn_].virtualPage = nEntry -> virtualPage;
    ipt[ppn_].pid = nEntry -> pid;
    ipt[ppn_].dirty = nEntry ->dirty;
    ipt[ppn_].valid = TRUE;
    ipt[ppn_].readOnly = nEntry -> readOnly;
    ipt[ppn_].use = nEntry -> use;
    ipt[ppn_].age = nEntry ->age;
    ipt[ppn_].pageLocation = nEntry ->pageLocation;
    ipt[ppn_].pageLocation = IPTEntry::LOC_MEMORY;
    ipt[ppn_].pageType = nEntry ->pageType;
    ipt[ppn_].swapOffset = nEntry ->swapOffset;

    // printIPT();
    // DEBUG('D', "IPTManager: addIPTEntry end. PID=%d, VPN=%d\n", nEntry -> pid, nEntry->virtualPage);

    iptLock -> Release();

    return 1;
}

int IPTManager::FreeAnyPage(int pid) {

    int page = 0;

    // iptLock -> Acquire() ;

    // DEBUG('E', "IPTManager: FreeAnyPage start for PID %d. \n", pid);

    if (PAGE_REPLACEMENT_POLICY == 1) {

        // this is FIFO replacement page. Find the oldest page.
        for (int i = 1; i < NumPhysPages; i++) {
            if (ipt[page].age > ipt[i].age) {
                page = i;
            }
        }
       // DEBUG('E', "IPTManager: Evicting Page:%d, PPN:%d, VPN=%d AGE:%d, PID:%d, PRP = FIFO.\n", page, ipt[page].physicalPage, ipt[page].virtualPage, ipt[page].age, ipt[page].pid);
    } else if (PAGE_REPLACEMENT_POLICY == 2) {
        // this is a random replacement policy
        
        page = abs(rand() % NumPhysPages);
        // DEBUG('E', "IPTManager: Evicting Page:%d, PPN:%d, VPN=%d AGE:%d, PID:%d, PRP = RAND.\n", page, ipt[page].physicalPage, ipt[page].virtualPage, ipt[page].age, ipt[page].pid);
    }

    if (ipt[page].valid == TRUE) {
        //update the dirty bit from TLB if in ther.
        IntStatus ints = interrupt -> SetLevel(IntOff);

        for (int i = 0; i < TLBSize; i++) {
            if ( machine->tlb[i].physicalPage == page && machine->tlb[i].valid == TRUE) {                
                ipt[page].dirty = machine -> tlb[i].dirty;
                machine -> tlb[i].valid = FALSE;
                // DEBUG ('E', "IPTManager: Entry about to evict found in TLB at index=%d, Dirty eval=%d\n", i, ipt[page].dirty) ;
                break;
            }
        }
        //restore interrupts
        interrupt->SetLevel(ints);

        ipt[page].valid = FALSE;

        // printf ("IPTManager:Entry about to evict: ") ;
        // ipt[page].printIPTEntry() ;
        
        int swapLoc = -1;
        // evict the page into SWAP file, if dirty

        if (ipt[page].dirty == TRUE) {

            // DEBUG('E', "IPTManager:Evicting PPN=%d, this belongs to PID=%d\n", page, ipt[page].pid);
            //write the page into the swap file.
            swapLoc = sfm -> writePageToSwapFile(page);
        }
        // update the page-table in address-space
        Process* p = pm -> getProcess(ipt[page].pid);
        if (p != NULL) {
            p -> getProcessAddressSpace() -> markPageAsEvicted(ipt[page].virtualPage, swapLoc, page, ipt[page].dirty);
        }
    }

    // DEBUG('E', "IPTManager: FreeAnyPage end for PID:%d. \n", pid);

    // iptLock -> Release();

    return page;

}

int IPTManager::markAsDirty(int pid, int vpn, bool dirty_, int ppn , int loc) {
    int i = 0;
    
    // DEBUG('D', "IPTManager: markAsDirty start PID:%d, VPN:%d.\n", pid, vpn);

    for (i = 0; i < NumPhysPages; i++) {
        if (ipt[i].pid == pid && ipt[i].virtualPage == vpn && ipt[i].valid == TRUE )
            break;
    }

    if (i < NumPhysPages) {
        //found an entry in IPT.
        ipt[i].dirty = dirty_;
       // DEBUG('T', "IPTManager: MarkAsDirty Entry = %d for PID=%d, VPN=%d. .\n", i, pid, vpn);

    }
    /*
    else {
        DEBUG('W', "IPTManager: Mark as dirty is called PID:%d , VPN:%d. But not found in IPT. Src=%d\n", pid, vpn, loc);
        currentThread -> space -> onOtherException() ;
        interrupt -> Halt() ;
    }
    */
    // DEBUG('D', "IPTManager: markAsDirty end. PID:%d, VPN:%d.\n", pid, vpn);

    
    return 1;
}

/*
int IPTManager::markAsInvalid(int pid, int vpn, bool invalid_) {
    int i = 0;
    DEBUG('I', "IPTManager: In markAsInvalid method.\n");
    DEBUG('D', "IPTManager: markAsInvalid start. PID:%d, VPN:%d.\n", pid, vpn);

    iptLock -> Acquire();
    // serach through IPT
    for (i = 0; i < NumPhysPages; i++) {
        if (ipt[i].pid == pid && ipt[i].virtualPage == vpn )
            break;
    }

    if (i < NumPhysPages) {
        DEBUG('I', "IPTManager: Entry %d valid bit updated successfully. PID: %d, VPN=%d\n", i, pid, vpn);
        ipt[i].valid = invalid_;

    } else {
        DEBUG('W', "IPTManager: ??????? MarkAsInvalid was called on PID:%d , VPN=%d but there;s no entry in ITP. Skipping marking. ???????? \n", pid, vpn);
    }

    DEBUG('D', "IPTManager: markAsInvalid end. PID:%d, VPN:%d.\n", pid, vpn);

    iptLock -> Release();

    return 1;
}
 */


int IPTManager::getPhysPage(int pid) {

    iptLock -> Acquire();
    // DEBUG('I', "IPTManager:Get physical page for PID=%d\n", pid);

    int freePage = physPages -> Find();
    while (freePage == -1) {
        // DEBUG('E', "\nIPTManager:Out of memory. Keeking somepage out for PID=%d.\n", pid);
        // no free page found. Evict any page.
        freePage = FreeAnyPage(pid);
    }
    // DEBUG('I', "IPTManager:Get physical page for PID=%d served. Returning PPN=%d\n", pid, freePage);

    iptLock -> Release();

    return freePage;
}

int IPTManager::freePhysPage(int pid, int vpn, int ppn) {

    iptLock -> Acquire();

    // DEBUG('I', "IPTManager:Free physical page PPN(%d) passed by PID=%d\n", ppn, pid);

    if (ipt[ppn].valid == FALSE) {
        // DEBUG('W', "IPTManager:??? FreePhysPage %d for PID=%d. But it's INVALID entry. \n", ppn, pid);
    }
    else {
        if (ppn < 0 || ppn >= NumPhysPages) {
            // DEBUG('W', "IPTManager:Invalid PPN(%d) passed by PID=%d for FreePhysPage\n", ppn, pid);
            iptLock->Release();
            return 1;
        }

        if(ipt[ppn].virtualPage != vpn || ipt[ppn].pid != pid) {
            // DEBUG('W', "IPTManager:FreePhysPage(). VPN or PID dont match. Passed VPN=%d, PID=%d, IPTEntry VPN=%d, PID=%d\n", vpn, pid, ipt[ppn].virtualPage, ipt[ppn].pid);
            iptLock->Release();
            return 1;
        }
        
        //clear the bitmap
        physPages-> Clear(ppn);

        //also invalidate the IPT
        ipt[ppn].valid = FALSE;

        // DEBUG('I', "IPTManager:PPN(%d) free success by PID=%d\n", ppn, pid);
    }
    iptLock -> Release();
    return 1;
}

